using System.Collections.Generic;
using System.Diagnostics;

namespace Dida.Dbg
{
	public interface IDebugMeshImpl
	{
		void Begin();
		void AddTriangle(Vector3D[] verts);
		void End();

		void Draw(Viewpoint viewpoint,Matrix4 mat);
	}

	/// <summary>
	/// A mesh that can be used for debugging purposes.
	/// </summary>
	/// <remarks>
	/// <h2>Specifying the mesh</h2>
	/// <para>
	/// The content of the mesh can be specified by adding the triangles using 
	/// the AddTriangle methods.
	/// </para>
	/// <para>
	/// Adding the triangles to the mesh is an initialization step,
	/// and should only be done once per <see cref = "DebugMesh"/>. This
	/// initialization step is started using the <see cref = "Begin"/> method,
	/// and closed using the <see cref = "End"/> method. While inside such a
	/// block, the triangles can be added using the various AddTriangle methods.
	/// </para>
	/// </remarks>
	public class DebugMesh : IGeometryDebugMesh
	{
		private IDebugMeshImpl impl;
		private bool inBeginEndBlock = false;

		internal DebugMesh(IDebugMeshImpl impl)
		{
			this.impl = impl;
		}

		/// <summary>
		/// Begin adding triangles to this mesh. See <see cref = "DebugMesh"/>
		/// for more information on how to add triangles.
		/// </summary>
		/// <seealso cref="End"/>
		/// <seealso cref="AddTriangle(Vector3D[])"/>
		public void Begin()
		{
			impl.Begin();
			inBeginEndBlock = true;
		}

		/// <summary>
		/// End adding triangles to this mesh. See <see cref = "DebugMesh"/>
		/// for more information on how to add triangles.
		/// </summary>
		/// <seealso cref="Begin"/>
		/// <seealso cref="AddTriangle(Vector3D[])"/>
		public void End()
		{
			impl.End();
			inBeginEndBlock = false;
		}

		/// <summary>
		/// Add a triangle to this mesh. This method should be called from inside
		/// a <see cref = "Begin"/> / <see cref = "End"/> block.
		/// </summary>
		/// <param name="verts">The vertices that form the triangle.</param>
		public void AddTriangle(Vector3D[] verts)
		{
			Debug.Assert(inBeginEndBlock, "AddTriangle must be called from inside a Begin/End block");
			impl.AddTriangle(verts);
		}

		/// <summary>
		/// Add a triangle to this mesh. This method should be called from inside
		/// a <see cref = "Begin"/> / <see cref = "End"/> block.
		/// </summary>
		/// <param name="v1">The first vertex of the triangle.</param>
		/// <param name="v2">The second vertex of the triangle.</param>
		/// <param name="v3">The third vertex of the triangle.</param>
		public void AddTriangle(Vector3D v1, Vector3D v2, Vector3D v3)
		{
			Debug.Assert(inBeginEndBlock, "AddTriangle must be called from inside a Begin/End block");
			impl.AddTriangle(new Vector3D[] { v1, v2, v3 });
		}

		/// <summary>
		/// Add a triangle to this mesh. This method should be called from inside
		/// a <see cref = "Begin"/> / <see cref = "End"/> block.
		/// </summary>
		/// <param name="tri">The triangle to add.</param>
		public void AddTriangle(CollisionTriangle tri)
		{
			Debug.Assert(inBeginEndBlock, "AddTriangle must be called from inside a Begin/End block");
			impl.AddTriangle(new Vector3D[]{tri.GetVertex(0),
				tri.GetVertex(1), tri.GetVertex(2)});
		}

		public void AddTriangles(ICollection<CollisionTriangle> triangles)
		{
			Debug.Assert(inBeginEndBlock, "AddTriangles must be called from inside a Begin/End block");
			foreach (CollisionTriangle tri in triangles)
				AddTriangle(tri);
		}

		public void AddTriangle(MeshLoader.Triangle tri)
		{
			Debug.Assert(inBeginEndBlock, "AddTriangle must be called from inside a Begin/End block");
			impl.AddTriangle(new Vector3D[]{tri.Vertices[0].pos,
				tri.Vertices[1].pos, tri.Vertices[2].pos});
		}

		public void AddTriangles(ICollection<MeshLoader.Triangle> triangles)
		{
			Debug.Assert(inBeginEndBlock, "AddTriangles must be called from inside a Begin/End block");
			foreach (MeshLoader.Triangle tri in triangles)
				AddTriangle(tri);
		}

		public void AddPolygon(Polygon pol)
		{
			Debug.Assert(inBeginEndBlock, "AddPolygon must be called from inside a Begin/End block");
			Polygon[] tris = pol.Triangulate();
			foreach (Polygon tri in tris)
			{
				impl.AddTriangle(new Vector3D[] {tri.GetVertex(0),
					tri.GetVertex(1), tri.GetVertex(2)});
			}
		}

		public void AddPolygons(ICollection<Polygon> polygons)
		{
			Debug.Assert(inBeginEndBlock, "AddPolygons must be called from inside a Begin/End block");
			foreach (Polygon pol in polygons)
				AddPolygon(pol);
		}

		public IDebugMeshImpl Implementation
		{
			get { return impl; }
		}
	}
}